﻿Imports System.Drawing.Printing

Public MustInherit Class ReportPrintDocument

    '打印事件回调事件与对象
    '打印结束通知外部
    Public Delegate Sub EndPrintCallBack(ByVal sender As Object, ByVal e As PrintCallBackEventArgs)
    Private m_EndCallBack As EndPrintCallBack

    '报表数据源
    'Private m_DataSourceList As Object  'List(Of Object)

    '默认纸张
    Private m_DefaultPageSize As Printing.PaperSize

    '页边距设置
    Private m_LeftMargin As Integer
    Private m_RightMargin As Integer
    Private m_TopMargin As Integer
    Private m_BottomMargin As Integer

    '页面总体打印宽度，报表的总宽度，而不是纸张宽度
    Private m_PageWidth As Single
    '一行数据的总宽度，主要针对页面中有表格的部分
    Private m_RowWidth As Single

    '页眉部分
    Private m_PageHeader As ReportSectionPrintElement
    '页脚部分
    Private m_PageFooter As ReportSectionPrintElement
    '页面正文部分
    Private m_PageBody As ReportSectionPrintElement

    '默认字体设置
    Private m_DefaultFont As Font
    '默认笔刷设置
    Private m_DefaultBrush As SolidBrush ' = New SolidBrush(Color.Black)
    Private m_DefaultPen As Pen '= New Pen(New SolidBrush(Color.Black))

    '分辨率设定
    Protected _dpiX As Single = 96
    Protected _dpiY As Single = 96
    '总页数与当前打印页
    Protected _TotalPages As Integer
    Protected _CurrentPage As Integer
    '每页可打印行数
    Protected _RowsOfPage As Integer
    '当前正在打印的记录索引，对应数据源的索引
    Protected _CurrentItemIndex As Integer
    '每页的绘制点
    Protected _CurrentPoint As Point

#Region "构造函数"

    Public Sub New()
        'Private m_EndCallBack As EndPrintCallBack

        '报表数据源
        'Private m_DataSourceList As List(Of Object)

        '默认纸张大小:12.5cm * 24cm
        m_DefaultPageSize = New Printing.PaperSize("自定义凭证纸", 492, 846)

        '页边距设置
        m_LeftMargin = 10
        m_RightMargin = 10
        m_TopMargin = 20
        m_BottomMargin = 20

        '页面总体打印宽度，报表的总宽度，而不是纸张宽度
        m_PageWidth = m_DefaultPageSize.Width
        '一行数据的总宽度，主要针对页面中有表格的部分
        m_RowWidth = m_PageWidth

        '页眉部分
        m_PageHeader = New ReportSectionPrintElement()
        '页脚部分
        m_PageFooter = New ReportSectionPrintElement()
        '页面正文部分
        m_PageBody = New ReportSectionPrintElement()

        '默认字体设置
        m_DefaultFont = New Font("宋体", 8, FontStyle.Regular)

        '笔刷设置
        m_DefaultBrush = New SolidBrush(Color.Black)
        m_DefaultPen = New Pen(New SolidBrush(Color.Black))

        '分辨率设定
        _dpiX = 96
        _dpiY = 96

        '总页数与当前打印页
        _TotalPages = 0
        _CurrentPage = 0

        '每页可打印行数
        _RowsOfPage = 0

        '当前正在打印的记录索引，对应数据源的索引
        _CurrentItemIndex = -1 '默认是非法索引，这样可以避免没有数据时出错

        '每页的绘制点，默认起始点为左上角
        _CurrentPoint.X = m_LeftMargin
        _CurrentPoint.Y = m_TopMargin

        '宽度设置:厘米 * 567 = twips / 1440 = 英寸 * _dpiX =像素

    End Sub
#End Region

#Region "属性对象"

    'Public Property DataSourceList As Object
    '    Get
    '        Return m_DataSourceList
    '    End Get
    '    Set(value As Object)
    '        m_DataSourceList = value

    '        If value IsNot Nothing Then
    '            StatisticsPages()
    '        End If
    '    End Set
    'End Property

    Public Property EndCallBack As EndPrintCallBack
        Get
            Return m_EndCallBack
        End Get
        Set(value As EndPrintCallBack)
            m_EndCallBack = value
        End Set
    End Property

    Public Property DefaultPageSize As PaperSize
        Get
            Return m_DefaultPageSize
        End Get
        Set(value As PaperSize)
            m_DefaultPageSize = value
        End Set
    End Property

    Public Property LeftMargin As Integer
        Get
            Return m_LeftMargin
        End Get
        Set(value As Integer)
            m_LeftMargin = value
        End Set
    End Property

    Public Property RightMargin As Integer
        Get
            Return m_RightMargin
        End Get
        Set(value As Integer)
            m_RightMargin = value
        End Set
    End Property

    Public Property TopMargin As Integer
        Get
            Return m_TopMargin
        End Get
        Set(value As Integer)
            m_TopMargin = value
        End Set
    End Property

    Public Property BottomMargin As Integer
        Get
            Return m_BottomMargin
        End Get
        Set(value As Integer)
            m_BottomMargin = value
        End Set
    End Property

    Public Property PageWidth As Single
        Get
            Return m_PageWidth
        End Get
        Set(value As Single)
            m_PageWidth = value
        End Set
    End Property

    Public Property RowWidth As Single
        Get
            Return m_RowWidth
        End Get
        Set(value As Single)
            m_RowWidth = value
        End Set
    End Property

    Public Property PageHeader As ReportSectionPrintElement
        Get
            Return m_PageHeader
        End Get
        Set(value As ReportSectionPrintElement)
            m_PageHeader = value
        End Set
    End Property

    Public Property PageFooter As ReportSectionPrintElement
        Get
            Return m_PageFooter
        End Get
        Set(value As ReportSectionPrintElement)
            m_PageFooter = value
        End Set
    End Property

    Public Property PageBody As ReportSectionPrintElement
        Get
            Return m_PageBody
        End Get
        Set(value As ReportSectionPrintElement)
            m_PageBody = value
        End Set
    End Property

    Public Property DefaultFont As Font
        Get
            Return m_DefaultFont
        End Get
        Set(value As Font)
            m_DefaultFont = value
        End Set
    End Property

    Public Property DefaultBrush As SolidBrush
        Get
            Return m_DefaultBrush
        End Get
        Set(value As SolidBrush)
            m_DefaultBrush = value
        End Set
    End Property

    Public Property DefaultPen As Pen
        Get
            Return m_DefaultPen
        End Get
        Set(value As Pen)
            m_DefaultPen = value
        End Set
    End Property

#End Region

#Region "统计分页"
    'Public Function SetDataSource(Of T)(ByVal v As List(Of T)) As List(Of T)

    'End Function


    Protected MustOverride Sub StatisticsPages()

#End Region

#Region "打印预览"
    ''' <summary>
    ''' 打印预览科目汇总，返回科目汇总报表
    ''' </summary>
    ''' <returns></returns>
    Public Function PrintReport() As Printing.PrintDocument
        Dim printDoc As Printing.PrintDocument = New Printing.PrintDocument()
        '取出纸张设置
        If My.Settings.VoucherPageSettings IsNot Nothing Then
            printDoc.DefaultPageSettings = My.Settings.VoucherPageSettings
        End If
        '设置总页数
        printDoc.DocumentName = _TotalPages.ToString()

        AddHandler printDoc.PrintPage, AddressOf PrintPage
        AddHandler printDoc.BeginPrint, AddressOf BeginPrint
        AddHandler printDoc.EndPrint, AddressOf EndPrint

        Return printDoc

    End Function
#End Region

#Region "打印事件"
    Protected Sub PrintPage(ByVal sender As Object, ByVal e As Printing.PrintPageEventArgs)

        'Debug.Print("当前打印页：" & _CurrentPage)

        '当前页数累加,当前页数从1算起
        _CurrentPage += 1

        '当前绘制起始点
        _CurrentPoint = New Point(m_LeftMargin, m_TopMargin)

        '如果是第一页，则记录索引为0
        _CurrentItemIndex = (_CurrentPage - 1) * _RowsOfPage

        '画页标题
        DrawPageHeader(e)
        '画会计期间
        DrawPageBody(e)
        '画公司名称
        DrawPageFooter(e)

        '判断是否是最后一页
        '当前打印的是第几页？
        If _CurrentPage >= _TotalPages Then
            '最后一页
            '设置打印完毕
            e.HasMorePages = False
            '重置打印当前页
            _CurrentPage = 0
            '重置凭证列表索引
            _CurrentItemIndex = 0
        Else
            '还有
            e.HasMorePages = True
        End If

    End Sub

    Private Sub BeginPrint(ByVal sender As Object, ByVal e As Printing.PrintEventArgs)
        '
        'Debug.Print("开始打印：" & _CurrentPage)
        '重置打印变量
        _CurrentPage = 0

        '打印不能取消
        e.Cancel = False

    End Sub

    Private Sub EndPrint(ByVal sender As Object, ByVal e As Printing.PrintEventArgs)

        If m_EndCallBack IsNot Nothing Then
            Dim ec As PrintCallBackEventArgs = New PrintCallBackEventArgs()
            ec.Msg = "打印完毕。"
            ec.PrintStatus = True

            m_EndCallBack.Invoke(Me, ec)

        End If
    End Sub
#End Region

#Region "绘制页面过程"
    Protected MustOverride Sub DrawPageHeader(ByVal e As Printing.PrintPageEventArgs)
    Protected MustOverride Sub DrawPageBody(ByVal e As Printing.PrintPageEventArgs)
    Protected MustOverride Sub DrawPageFooter(ByVal e As Printing.PrintPageEventArgs)
#End Region

#Region "通用绘制过程"

    ''' <summary>
    ''' 绘制单元格内容
    ''' </summary>
    ''' <param name="e">打印页面参数</param>
    ''' <param name="v">打印的字串值</param>
    ''' <param name="rect">字串打印的矩形</param>
    ''' <param name="align">字串的对齐方式</param>
    Protected Sub DrawCellValue(ByVal e As Printing.PrintPageEventArgs, ByVal font As Font, ByVal brush As Brush, ByVal v As String, ByVal rect As Rectangle, ByVal align As ContentAlignment)
        '求字串宽高
        Dim strSizeF As SizeF = e.Graphics.MeasureString(v, font)
        '计算需要几行？
        Dim needLines As Integer = 1
        ' = IIf(rect.Width Mod strSizeF.Width = 0, rect.Width \ strSizeF.Width, rect.Width \ strSizeF.Width + 1)
        If strSizeF.Width > rect.Width Then
            '计算行数
            needLines = IIf(strSizeF.Width Mod rect.Width = 0, strSizeF.Width \ rect.Width, strSizeF.Width \ rect.Width + 1)
        End If
        '计算可容纳行数
        Dim allowLinews As Integer = rect.Height \ strSizeF.Height
        '判断可打印行数
        Dim lines As Integer = IIf(needLines > allowLinews, allowLinews, needLines)
        '打印数组
        Dim strs(lines - 1) As String

        Dim charIndex As Integer = 0
        For i As Integer = 0 To lines - 1
            strs(i) = ""
            For j As Integer = charIndex To v.Length - 1
                strs(i) &= v.Substring(j, 1)
                '判断长度是否达到了宽度值
                If e.Graphics.MeasureString(strs(i), font).Width > rect.Width Then
                    '退回一个字符
                    strs(i) = v.Substring(charIndex, strs(i).Length - 1)

                    charIndex = j

                    Exit For
                End If
            Next
        Next
        '开始打印输出
        Dim startX As Integer = rect.X
        Dim startY As Integer = rect.Y + (rect.Height - strSizeF.Height * lines) / 2 '第一行起始点,即纵向居中
        Dim outX As Single  '每行输出的X点
        Dim outY As Single  '每行输出的Y点

        For z As Integer = 0 To lines - 1
            'X计算要看对齐方式
            Select Case align
                Case ContentAlignment.MiddleCenter
                    outX = startX + rect.Width / 2 - e.Graphics.MeasureString(strs(z), font).Width / 2
                Case ContentAlignment.MiddleLeft
                    outX = startX
                Case ContentAlignment.MiddleRight
                    outX = startX + rect.Width - e.Graphics.MeasureString(strs(z), font).Width
                    '粗体会造成宽度不合适，需要手动增加
                    If font.Bold Then
                        outX -= 2
                    End If
                Case Else
                    outX = startX
            End Select

            outY = startY + z * strSizeF.Height
            e.Graphics.DrawString(strs(z), font, brush, outX, outY)
        Next

    End Sub

#End Region
End Class
